home *** CD-ROM | disk | FTP | other *** search
/ MacFormat España 15 / macformat_15.iso / Shareware Internet / Juegos / Bolo / More information / Sample Code / BoloInfoPacket.c
C/C++ Source or Header  |  1995-04-25  |  8KB  |  223 lines

  1. /* Sample code to access Bolo's game infomation packet interface */
  2. /* (C) 1993-1995 Stuart Cheshire <cheshire@cs.stanford.edu>
  3. /* Unix C file -- tabs are 8 spaces -- compile with gcc for best results */
  4. /* run with "a.out <port-number>" to listen on a UDP port for games starting */
  5. /* run with "a.out <port-number> <hostname>" to ping a running Bolo game and
  6.    then wait for replies. */
  7. /* run with "a.out <port-number> <subnet-broadcast-address> to locate ALL
  8.    Bolo games running within a particular IP broadcast domain. */
  9. /* There is no reliability here -- this is just to illustrate how to send and
  10.    receive the packets. You may wish to do retransmissions and timeouts. */
  11.  
  12. #include <stdio.h>
  13. #include <sys/types.h>
  14. #include <sys/socket.h>
  15. #include <netinet/in.h>
  16. #include <netdb.h>
  17. #include <sys/time.h>
  18.  
  19. /* Bolo uses little-endian byte ordering -- like the BBC micro's 6502
  20. processor, 80x86 series, Acorn Risk Machine, and DEC computers, and
  21. unlike Macs and Suns. The following macro should convert to and from
  22. local byte ordering on any unix machine. I don't know a better way to
  23. do it, but a good compiler will optimize it out anyway. */
  24. #define netshort(X) (htons(X) >> 8 | htons(X) << 8)
  25.  
  26. /* Macs count time since Midnight, 1st Jan 1904. Unix counts from 1970.
  27.    This value adjusts for the 66 years and 17 leap-days difference. */
  28. #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60)
  29.  
  30. /* Also note the following two bugs in Bolo 0.99, 0.99.1 and 0.99.2:
  31.  
  32. 1. The GameID was intended to be simply an opaque identifier to
  33. distinguish games. Hence I did not make Bolo put the components in
  34. canonical network byte order, since IDs were not supposed to be
  35. disassembled, but just compared for equality or inequality. However, I
  36. changed my mind and decided that it is useful (or at least interesting)
  37. to know when and where a game started. So, for now, the game timestamp is
  38. in the wrong byte order. This will be fixed in a future version of Bolo.
  39.  
  40. 2. The Mac system call "GetDateTime" returns the time in LOCAL time,
  41. not GMT. Bolo neglects to convert this time to GMT before sending it
  42. over the network, resulting in the game start time being given in local
  43. time for the Mac that started it. There is no way at present to convert
  44. this time to a real time, except if you happen to know what time zone
  45. the offending Mac is in. This will be fixed in a future version of Bolo. */
  46.  
  47. /* Bolo 0.99.3 and 0.99.4 were not publicly released */
  48. /* Bolo 0.99.5 fixes the GMT problem but not the byte ordering. This
  49. decision was made so as not to break the existing bolo tracker code.
  50. When a future version of the Bolo Information interface is designed
  51. the byte ordering (and many other things) will be corrected. */
  52.  
  53. /* Convert Bolo's pascal string to C string so we can work with it easily */
  54. static void PtoCStr(unsigned char *string)
  55.     {
  56.     int i, len = string[0];
  57.     for (i=0; i<len; i++) string[i] = string[i+1];
  58.     string[len] = 0;
  59.     }
  60.  
  61. typedef struct { u_char c[36]; } u_char36;
  62. typedef u_char  BYTE;
  63. typedef u_short WORD;
  64.  
  65. #define BoloVersion_Major    0x00
  66. #define BoloVersion_Minor    0x99
  67. #define BoloVersion_Revision 0x05
  68.  
  69. #define BoloPacket_Request   13
  70. #define BoloPacket_Response  14
  71.  
  72. typedef struct
  73.     {
  74.     BYTE signature[4];    /* 'Bolo' */
  75.     BYTE versionMajor;    /* 0x00   */
  76.     BYTE versionMinor;    /* 0x99   */
  77.     BYTE versionRevision;    /* 0x05   */
  78.     BYTE type;        /* 13 for request, 14 for response */
  79.     } BOLOHEADER;
  80.  
  81. typedef struct
  82.     {
  83.     struct in_addr firstmachine;
  84.     u_long start_time;
  85.     } GAMEID;
  86.  
  87. typedef struct
  88.     {
  89.     BOLOHEADER h;
  90.  
  91.     u_char36 mapname;    /* Pascal string (first byte is length)         */
  92.     GAMEID gameid;         /* 8 byte unique ID for game (combination       */
  93.                  /* of starting machine address & timestamp)     */
  94.     BYTE gametype;         /* Game type (1, 2 or 3: open, tourn. & strict) */
  95.     BYTE allow_mines;    /* 0x80 for normal hidden mines                 */
  96.                  /* 0xC0 for all mines visible                   */
  97.     BYTE allow_AI;         /* 0 for no AI tanks, 1 for AI tanks allowed    */
  98.     BYTE spare1;         /* 0                                            */
  99.     long start_delay;    /* if non zero, time until game starts, (50ths) */
  100.     long time_limit;     /* if non zero, time until game ends, (50ths)   */
  101.  
  102.     WORD num_players;    /* number of players                            */
  103.     WORD free_pills;     /* number of free (neutral) pillboxes           */
  104.     WORD free_bases;     /* number of free (neutral) refuelling bases    */
  105.     BYTE has_password;   /* non-zero if game has password set            */
  106.     BYTE spare2;         /* 0                                            */
  107.     } INFO_PACKET;
  108.  
  109. static void sendquery(int s, char **argv)
  110.     {
  111.     static const BOLOHEADER h =
  112.         {
  113.         { "Bolo" },
  114.         BoloVersion_Major,
  115.         BoloVersion_Minor,
  116.         BoloVersion_Revision,
  117.         BoloPacket_Request
  118.         };
  119.     struct sockaddr_in rmtaddr;
  120.     rmtaddr.sin_family      = AF_INET;
  121.     rmtaddr.sin_port        = htons(atoi(argv[1]));
  122.     rmtaddr.sin_addr.s_addr = inet_addr(argv[2]);
  123.     if (rmtaddr.sin_addr.s_addr == -1)
  124.         {
  125.            struct hostent *hp = gethostbyname(argv[2]);
  126.            if (!hp) { fprintf(stderr, "%s: unknown host\n", argv[2]); exit(1); }
  127.            rmtaddr.sin_family = hp->h_addrtype;
  128.            bcopy(hp->h_addr, (char *) &rmtaddr.sin_addr, hp->h_length);
  129.         }
  130.  
  131.            sendto(s, &h, sizeof(h), 0, (struct sockaddr *)&rmtaddr, sizeof(rmtaddr));
  132.     }
  133.  
  134. static void printip(struct in_addr address)
  135.     {
  136.     union { BYTE b[4]; struct in_addr address; } a;
  137.     a.address = address;
  138.     printf("%d.", a.b[0]);
  139.     printf("%d.", a.b[1]);
  140.     printf("%d.", a.b[2]);
  141.     printf("%d" , a.b[3]);
  142.     }
  143.  
  144. int main(int argc, char **argv)
  145.     {
  146.     int s;
  147.     struct sockaddr_in lcladdr;
  148.     u_short localport = 0;
  149.  
  150.        if (argc != 2 && argc != 3)
  151.         {
  152.         fprintf(stderr, "Usage: %s <local port>\n", argv[0]);
  153.         fprintf(stderr, "Usage: %s <remote port> <remote machine>\n", argv[0]);
  154.         exit(1);
  155.         }
  156.  
  157.     /* Open socket */
  158.     s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  159.     if (s < 0) { perror("socket"); exit(1); }
  160.  
  161.     /* Bind socket to local UDP port number */
  162.     lcladdr.sin_family      = AF_INET;
  163.     if (argc==2) lcladdr.sin_port = htons(atoi(argv[1]));
  164.     else         lcladdr.sin_port = 0;
  165.     lcladdr.sin_addr.s_addr = INADDR_ANY;
  166.     if (bind(s, (struct sockaddr *)&lcladdr, sizeof(lcladdr)) < 0) { perror("bind"); exit(1); }
  167.     if (argc==3) sendquery(s, argv);
  168.  
  169.     /* Receive the data */
  170.     while(1)
  171.         {
  172.         time_t timenow, gametime;
  173.         char *timestring, *ptr;
  174.         INFO_PACKET info;
  175.         struct sockaddr_in from;
  176.         int fromlen = sizeof(from);
  177.         int packetlen = recvfrom(s, &info, sizeof(info), 0, (struct sockaddr *)&from, &fromlen);
  178.         if (packetlen != sizeof(info)) continue;
  179.         if (info.h.signature[0]    != 'B'  ||
  180.             info.h.signature[1]    != 'o'  ||
  181.             info.h.signature[2]    != 'l'  ||
  182.             info.h.signature[3]    != 'o'  ||
  183.             info.h.versionMajor    != BoloVersion_Major    ||
  184.             info.h.versionMinor    != BoloVersion_Minor    ||
  185.             info.h.type            != BoloPacket_Response   ) continue;
  186.         
  187.         PtoCStr(info.mapname.c);
  188.         info.num_players = netshort(info.num_players);
  189.         info.free_pills  = netshort(info.free_pills);
  190.         info.free_bases  = netshort(info.free_bases);
  191.         time(&timenow);
  192.         gametime = ntohl(info.gameid.start_time) - TIME_ADJUST;
  193.  
  194.         printf("%s", ctime(&timenow));
  195.  
  196.                printf("Bolo player at ");
  197.         printip(from.sin_addr);
  198.         printf(" on map \"%s\" running since\n", info.mapname.c);
  199.  
  200.         ptr = timestring = asctime(gmtime(&gametime));
  201.         while (*ptr && *ptr != '\n') ptr++;
  202.         *ptr = 0;
  203.         printf("%s", timestring);
  204.         if (info.h.versionRevision < 5) printf(" (local time for Mac at ");
  205.         else printf(" GMT (Started by Mac at ");
  206.         printip(info.gameid.firstmachine);
  207.         printf(")\n");
  208.  
  209.         printf("Options: ");
  210.         switch(info.gametype)
  211.             {
  212.             case 1: printf("Open Game"); break;
  213.             case 2: printf("Tournament"); break;
  214.             case 3: printf("Strict Tournament"); break;
  215.             }
  216.         if (info.allow_mines == 0x80) printf(", Hidden mines");
  217.         if (info.allow_AI) printf(", AI tanks allowed");
  218.         if (info.has_password) printf(", Password set");
  219.         printf("\nPlayers in game:%2d, Neutral pillboxes:%2d, Neutral Refueling Bases:%2d\n\n", info.num_players, info.free_pills, info.free_bases);
  220.         fflush(stdout);
  221.         }
  222.     }
  223.